using System;
using System.Collections.Generic;
using System.Text;
using System.IO.Ports;
using System.Threading;

namespace Planar.PT120
{
    public sealed class Driver : IDisposable
    {
        #region Member fields and properties
        private string _Name = "COM1";
        public string Name
        {
            get {return _Name; }
            set
            {
                if (value != null && value != _Name)
                {
                    //check to see if we should restart after name change.
                    bool restart = IsRunning;

                    //stop port if currently open
                    Stop();

                    _Name = value;

                    if (restart) Start();
                }
            }
        }
        private SerialPort _Port = null;

        public bool IsRunning
        {
            get { return (_Port!=null ? _Port.IsOpen : false); }
        }

        public static string[] GetPortNames()
        {
            return SerialPort.GetPortNames();
        }
        #endregion

        #region Constructors
        private Driver()
        {
        }

        private static Driver _Instance = null;
        public static Driver Instance
        {
            get
            {
                if (_Instance == null)
                {
                    _Instance = new Driver();
                }
                return _Instance;
            }
        }
        #endregion

        #region Start / Stop
        public void Start()
        {
            try
            {
                if (_Port == null)
                {
                    _Port = new SerialPort(Name);
                    _Port.NewLine = "\r";
                    _Port.ErrorReceived += new SerialErrorReceivedEventHandler(Port_ErrorReceived);
                    _Port.DataReceived += new SerialDataReceivedEventHandler(Port_DataReceived);
                }

                if (_Port != null && !_Port.IsOpen)
                {
                    _Port.Open();
                }
            }
            catch (Exception ex)
            {
                OnDriverError("Error starting driver", ex);
            }
        }
        public void Stop()
        {
            try
            {
                if (_Port != null && _Port.IsOpen)
                {
                    _Port.ErrorReceived -= new SerialErrorReceivedEventHandler(Port_ErrorReceived);
                    _Port.DataReceived -= new SerialDataReceivedEventHandler(Port_DataReceived);

                    _Port.Close();
                    _Port = null;
                }
            }
            catch (Exception ex)
            {
                OnDriverError("Error stopping driver", ex);
            }
        }
        #endregion

        #region Event Handlers
        void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (e.EventType == SerialData.Chars)
            {
                try
                {
                    string data = _Port.ReadLine();
                    Command cmd = new Command(data);
                    if (cmd.CommandType != CommandType.Unknown)
                    {
                        OnCommandReceived(cmd);
                    }
                }
                catch (Exception ex)
                {
                    OnDriverError("Error receiving input", ex);
                }
            }
        }

        void Port_ErrorReceived(object sender, SerialErrorReceivedEventArgs e)
        {
            OnDriverError("Error reading serial port: " + e.EventType.ToString());
        } 
        #endregion

        #region Driver events
        public event EventHandler<CommandEventArgs> CommandReceived;
        private void OnCommandReceived(Command cmd)
        {
            if (CommandReceived != null) CommandReceived(this, new CommandEventArgs(cmd));
        }
        public event EventHandler<DriverErrorEventArgs> DriverError;
        private void OnDriverError(string Message, Exception ex)
        {
            if (DriverError != null) DriverError(this, new DriverErrorEventArgs(Message, ex));
        }
        private void OnDriverError(string Message)
        {
            OnDriverError(Message, null);
        }
        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            //This is just here to make sure the app will still close properly.
            try
            {
                Stop();
            }
            catch (Exception ex) 
            {
                System.Diagnostics.Debug.WriteLine(ex);
            }
        }

        #endregion

        #region object overrides
        public override string ToString()
        {
            return "PT120-" + _Name;
        } 
        #endregion
    }
}
